package com.frikinet.gestorepub.web.spring.controllers;

import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.http.HttpServletResponse;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.frikinet.gestorepub.model.Author;
import com.frikinet.gestorepub.model.Book;
import com.frikinet.gestorepub.model.Book.Status;
import com.frikinet.gestorepub.model.Category;
import com.frikinet.gestorepub.model.Editor;
import com.frikinet.gestorepub.model.Serie;
import com.frikinet.gestorepub.web.spring.services.PersistenceService;
import com.google.appengine.api.datastore.Link;
import com.google.appengine.api.datastore.Text;
import com.google.appengine.api.taskqueue.Queue;
import com.google.appengine.api.taskqueue.QueueFactory;
import com.google.appengine.api.taskqueue.TaskAlreadyExistsException;
import com.google.appengine.api.taskqueue.TaskOptions;
import com.google.appengine.api.taskqueue.TaskOptions.Method;

@Controller
@RequestMapping("/fetch")
public class FetchController
{
	private static final Logger log = Logger.getLogger(FetchController.class.getCanonicalName());
	private static final int TIMEOUT = 60000;
	private static final SimpleDateFormat idTimeFormat = new SimpleDateFormat("yyyyMMdd");
	
	@Autowired
	private PersistenceService persistenceService;
	
	@RequestMapping("/start")
	public void startFetching(PrintWriter out)
	{
		log.log(Level.INFO, "[Fetch] Start");
		Queue queue = QueueFactory.getQueue("FetchLetterQueue");
		for(char letter='a'; letter < 'z'; letter++)
		{
			queue.add(TaskOptions.Builder.withUrl("/app/fetch/letter/"+letter).taskName(String.format("task-letter-%s-%s", idTimeFormat.format(new Date()), letter)).method(Method.POST));
		}
		out.println("Fetching started");
	}
	
	@RequestMapping(value="/letter/{letter}", method=RequestMethod.POST)
	public void fetchLetter(@PathVariable String letter, HttpServletResponse response)
	{
		log.log(Level.INFO, String.format("[Fetch] [Letter] %s", letter));
		Queue queue = QueueFactory.getQueue("FetchAuthorQueue");
		try
		{
			for (Element authorLink: Jsoup.connect(String.format("http://www.epubgratis.me/obras/%s", letter)).timeout(TIMEOUT).get().select("ul.vocabindex.alphabetical a"))
			{
				Long id = getIdFromUrl(authorLink.attr("href"));
				queue.add(TaskOptions.Builder.withUrl(String.format("/app/fetch/author/%d", id)).taskName(String.format("task-author--%s-%d", idTimeFormat.format(new Date()), id)).method(Method.POST));
			}
			response.setStatus(200);
		}
		catch(IOException e)
		{
			log.log(Level.SEVERE, String.format("[Fetch] [Letter] %s [ERROR] %s", letter, e.getMessage()));
			response.setStatus(500);
		}
		catch(TaskAlreadyExistsException te)
		{
			
		}
	}
	
	@RequestMapping(value="/author/{id}", method=RequestMethod.POST)
	public void fetchAuthor(@PathVariable Long id, HttpServletResponse response)
	{
		log.log(Level.INFO, String.format("[Fetch] [Author] %s", id));
		Queue queue = QueueFactory.getQueue("FetchBookQueue");
		try
		{
			Element lastPager = Jsoup.connect(String.format("http://www.epubgratis.me/taxonomy/term/%d", id)).timeout(TIMEOUT).get().select("ul.pager li.pager-item").last();
			int maxPages = lastPager != null ? Integer.parseInt(lastPager.text()) : 0;
			for(int page = 0;page <= maxPages; page++)
			{
				//forEachPage
				log.info(String.format("[Fetch] [Author] %2$d [Page %1$d]", page, id));
				for(Element bookLink: Jsoup.connect(String.format("http://www.epubgratis.me/taxonomy/term/%d?page=%d", id, page)).timeout(TIMEOUT).get().select("a.eBook_titulo"))
				{
					Long idBook = getIdFromUrl(bookLink.attr("href"));
					queue.add(TaskOptions.Builder.withUrl(String.format("/app/fetch/book/%d", idBook)).taskName(String.format("task-book-%s-%d", idTimeFormat.format(new Date()), idBook)).method(Method.POST));
				}
			}
			response.setStatus(200);
		}
		catch(IOException e)
		{
			log.log(Level.SEVERE, String.format("[Fetch] [Author] %d [ERROR] %s", id, e.getMessage()));
			response.setStatus(500);
		}
		catch(TaskAlreadyExistsException te)
		{
			
		}
	}
	
	@RequestMapping(value="/book/{id}", method=RequestMethod.POST)
	public void fetchBook(@PathVariable Long id, HttpServletResponse response)
	{
		log.log(Level.INFO, String.format("[Fetch] [Book] %d", id));
		try
		{
			Document bookPage = Jsoup.connect(String.format("http://www.epubgratis.me/node/%d", id)).timeout(TIMEOUT).get();
			Book book = new Book();
			book.setId(id);
			
			//TITLE
			book.setTitle(bookPage.select("h1.eBook_titulo").text());
			
			//AUTHORS
			for(Element authorBookLink: bookPage.select("div.eBook_autor a"))
			{
				Author author = new Author();
				author.setId(getIdFromUrl(authorBookLink.attr("href")));
				author.setName(authorBookLink.text());
				book.getAuthors().add(author);
				book.getAuthorKeys().add(author.getId());
			}
			
			//CATEGORIES
			for(Element categoryLink: bookPage.select("div.eBook_genero div.field-item a"))
			{
				Category category = new Category();
				category.setId(getIdFromUrl(categoryLink.attr("href")));
				category.setName(categoryLink.text());
				book.getCategories().add(category);
				book.getCategoryKeys().add(category.getId());
			}
			
			//SERIE
			Element serieElement = bookPage.select("h3.eBook_coleccion").first();
			if(serieElement != null)
			{
				String infos[] = serieElement.ownText().split("- ");
				Serie serie = new Serie();
				serie.setId(getIdFromUrl(serieElement.select("a").attr("href")));
				serie.setName(serieElement.select("a").text());
				if(infos.length > 1)
					book.setSerieIndex(Integer.parseInt(infos[1]));
				book.setSerie(serie);
				book.setSerieKey(serie.getId());
			}
			
			//COVER
			Element coverImg = bookPage.select("div.eBook_portada img").first();
			if(coverImg != null)
				book.setCover(new Link(coverImg.attr("abs:src")));
			
			//LINK
			Element magnetLink = bookPage.select("div.eBook_descarga_enlace.magnet a").first();
			if(magnetLink != null)
				book.setLink(new Text(magnetLink.attr("href")));
			
			//VERSION
			Element versionString = bookPage.select("div.eBook_version b").first();
			if(versionString != null)
			{
				String parts[] = versionString.text().split("[.]");
				book.setMajorVersion(Integer.valueOf(parts[0]));
				book.setMinorVersion(Integer.valueOf(parts[1]));
			}
			
			//PAGES
			String pageCount = bookPage.select("div.eBook_paginas").text();
			if(!pageCount.equals(""))
				book.setPages(Integer.valueOf(pageCount));
			
			//EDITOR
			Element editorLink = bookPage.select("div.eBook_usuario a").first();
			if(editorLink != null)
			{
				Editor editor = new Editor();
				editor.setId(getIdFromUrl(editorLink.attr("href")));
				editor.setName(editorLink.text());
				book.setEditor(editor);
				book.setEditorKey(editor.getId());
			}
			
			SimpleDateFormat dateFormat = new SimpleDateFormat("dd.M.yy");
			
			//CREATION DATE
			Element pubDate = bookPage.select("div.eBook_creado").first();
			if(pubDate != null)
			{
				book.setPublicationDate(dateFormat.parse(pubDate.text()));
			}
			
			//MODIFICATION DATE
			Element modificationDate = bookPage.select("div.eBook_modificado").first();
			if(modificationDate != null)
			{
				book.setModificationDate(dateFormat.parse(modificationDate.text()));
			}
			else
			{
				book.setModificationDate(book.getPublicationDate());
			}
			
			//SYNOPSIS
			Element synopsis = bookPage.select("div.eBook_sinopsis").first();
			if(synopsis != null)
				book.setSynopsis(new Text(synopsis.html()));
			
			//NOTES
			Element notes = bookPage.select("div.eBook_notas_mid").first();
			if(notes != null)
				book.setNotes(new Text(notes.html()));
			
			//STATUS
			String codeStatus = bookPage.select("div.eBook_portada code").attr("class");
			boolean betaStatus = bookPage.select("div.eBook_div").hasClass("beta");
			if(codeStatus.equals("si"))
			{
				book.setStatus(Status.REVISED);
			}
			else if(codeStatus.equals("erratas"))
			{
				book.setStatus(Status.ERRORS);
			}
			else if(codeStatus.equals("no") && betaStatus)
			{
				book.setStatus(Status.SOON);
			}
			else if(codeStatus.equals("no") && !betaStatus)
			{
				book.setStatus(Status.NONE);
			}
			
			if(isBookUpdated(book))
			{
				book.setUpdateDate(new Date());
				persistenceService.saveBook(book);
			}
			
			response.setStatus(200);
		}
		catch(Exception e)
		{
			log.log(Level.SEVERE, String.format("[Fetch] [Book] %d [ERROR] %s", id, e.getMessage()));
			response.setStatus(500);
		}
	}
	
	private Long getIdFromUrl(String url)
	{
		String parts[] = url.split("/");
		return Long.parseLong(parts[parts.length-1]);
	}
	
	private boolean isBookUpdated(Book book)
	{
		Book oldBook = persistenceService.getBook(book.getId());
		
		if(oldBook == null)
		{
			return true;
		}
		
		if(oldBook.getMajorVersion() != null)
		{
			if(book.getMajorVersion() == null || oldBook.getMajorVersion().compareTo(book.getMajorVersion()) < 0)
				return true;
		}
		
		if(oldBook.getMinorVersion() != null)
		{
			if(book.getMinorVersion() == null || oldBook.getMinorVersion().compareTo(book.getMinorVersion()) < 0)
				return true;
		}
		
		if(!oldBook.getTitle().equals(book.getTitle()))
			return true;
		
		if(oldBook.getLink() != null)
		{
			if(book.getLink() == null || !oldBook.getLink().equals(book.getLink()))
				return true;
		}
		
		if(oldBook.getModificationDate().before(book.getModificationDate()))
			return true;
		
		return false;
	}
}
